package com.googlecode.scrptingdoclet.doclet;

import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.apache.commons.lang.ClassUtils;
import org.scannotation.AnnotationDB;
import org.scannotation.ClasspathUrlFinder;

import com.google.common.collect.Maps;
import com.googlecode.scrptingdoclet.evaluator.Evaluator;
import com.googlecode.scrptingdoclet.evaluator.annotation.ScriptEvaluator;
import com.googlecode.scrptingdoclet.transformer.Transformer;
import com.googlecode.scrptingdoclet.transformer.annotation.ModelTransformer;

public class Registry {

	private Map<String, Evaluator<?>> evaluatorMap = Maps.newHashMap();

	private Map<String, Transformer<?>> transformerMap = Maps.newHashMap();

	public void setup() throws IOException {
		AnnotationDB db = new AnnotationDB();
		db.setScanFieldAnnotations(false);
		db.setScanMethodAnnotations(false);
		db.setScanParameterAnnotations(false);
		db.setIgnoredPackages(new String[] { "java", "javax", "com.sum" });

		URL[] urls = ClasspathUrlFinder.findClassPaths();
		db.scanArchives(urls);

		Set<String> evaluators = db.getAnnotationIndex().get(
				ScriptEvaluator.class.getName());
		Set<String> transformers = db.getAnnotationIndex().get(
				ModelTransformer.class.getName());

		registEvaluator(evaluators);
		registTransformer(transformers);
	}

	private void registTransformer(Set<String> set) {
		for (String resolverClass : set) {
			try {
				Class<Transformer> clazz = ClassUtils.getClass(resolverClass);
				Transformer evaluator = clazz.newInstance();

				if (transformerMap.containsKey("default")) {
					Transformer alreadyResolved = transformerMap.get("default");
					int precedence = getAnnotation(alreadyResolved)
							.precedence();

					if (getAnnotation(evaluator).precedence() > precedence) {
						transformerMap.put("default", evaluator);
					}
				} else {
					transformerMap.put("default", evaluator);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void registEvaluator(Set<String> set) {
		for (String resolverClass : set) {
			try {
				Class<Evaluator> clazz = ClassUtils.getClass(resolverClass);
				Evaluator evaluator = clazz.newInstance();

				String engineType = getAnnotation(evaluator).engine();

				if (evaluatorMap.containsKey(engineType)) {
					Evaluator<?> alreadyResolved = evaluatorMap.get(engineType);
					int precedence = getAnnotation(alreadyResolved)
							.precedence();

					if (getAnnotation(evaluator).precedence() > precedence) {
						evaluatorMap.put(engineType, evaluator);
					}
				} else {
					evaluatorMap.put(engineType, evaluator);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public Evaluator getDefaultEvaluator() {
		return getEvaluator("ruby");
	}

	public Evaluator getEvaluator(String engine) {
		Evaluator evaluator;
		if (evaluatorMap.containsKey(engine)) {
			evaluator = evaluatorMap.get(engine);
		} else {
			evaluator = getDefaultEvaluator();
		}
		evaluator.setScriptEngine(getScriptEngine(engine));

		return evaluator;
	}

	public Transformer getTransformer() {
		Transformer transformer = transformerMap.get("default");
		return transformer;
	}

	private ScriptEvaluator getAnnotation(Evaluator evaluator) {
		ScriptEvaluator annotation = evaluator.getClass().getAnnotation(
				ScriptEvaluator.class);
		return annotation;
	}

	private ModelTransformer getAnnotation(Transformer transformer) {
		ModelTransformer annotation = transformer.getClass().getAnnotation(
				ModelTransformer.class);
		return annotation;
	}

	private ScriptEngine getScriptEngine(String engine) {
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine scriptEngine = manager.getEngineByName(engine);
		return scriptEngine;
	}
}
